// 块缓存系统
// 参考 xv6-riscv 实现

#include "types.h"
#include "riscv.h"
#include "defs.h"
#include "param.h"
#include "spinlock.h"
#include "fs/buf.h"
#include "fs/virtio.h"

extern void virtio_disk_rw(struct buf *b, int write);
extern void ramdisk_rw(struct buf *b, int write);

// 使用ramdisk还是virtio
static int use_ramdisk = 0;

void set_use_ramdisk(int use) {
  use_ramdisk = use;
}

struct {
  struct spinlock lock;
  struct buf buf[NBUF];

  // LRU 双向链表
  struct buf head;
} bcache;

void
binit(void)
{
  struct buf *b;

  initlock(&bcache.lock, "bcache");

  // 创建双向链表
  bcache.head.prev = &bcache.head;
  bcache.head.next = &bcache.head;
  
  // 初始化head，确保它不是有效的buf
  bcache.head.dev = 0xFFFF;  // 标记为无效
  bcache.head.blockno = 0xFFFF;
  bcache.head.valid = 0;
  bcache.head.disk = 0;
  bcache.head.refcnt = 0;
  
  for(b = bcache.buf; b < bcache.buf + NBUF; b++){
    // 初始化所有字段
    b->next = bcache.head.next;
    b->prev = &bcache.head;
    b->dev = 0;
    b->blockno = 0;
    b->valid = 0;
    b->disk = 0;
    b->refcnt = 0;
    initsleeplock(&b->lock, "buffer");
    bcache.head.next->prev = b;
    bcache.head.next = b;
  }
}

// 查找指定设备和块号的缓存块
// 如果未缓存，分配一个
static struct buf*
bget(uint dev, uint blockno)
{
  struct buf *b;

  acquire(&bcache.lock);

  // 查找已缓存的块
  for(b = bcache.head.next; b != &bcache.head; b = b->next){
    if(b->dev == dev && b->blockno == blockno){
      b->refcnt++;
      release(&bcache.lock);
      acquiresleep(&b->lock);
      return b;
    }
  }

  // 未找到，分配一个未使用的块
  // 从链表尾部开始查找（LRU）
  for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
    if(b->refcnt == 0) {
      // 从链表中移除
      b->next->prev = b->prev;
      b->prev->next = b->next;
      
      // 设置新的块信息
      b->dev = dev;
      b->blockno = blockno;
      b->valid = 0;
      b->disk = 0;
      b->refcnt = 1;
      
      // 重新插入到链表头部
      b->next = bcache.head.next;
      b->prev = &bcache.head;
      bcache.head.next->prev = b;
      bcache.head.next = b;
      
      release(&bcache.lock);
      acquiresleep(&b->lock);
      return b;
    }
  }
  release(&bcache.lock);
  panic("bget: no buffers");
}

// 返回一个已锁定的缓存块，包含指定设备和块号的数据
struct buf*
bread(uint dev, uint blockno)
{
  struct buf *b;

  b = bget(dev, blockno);
  if(b == 0)
    panic("bread: bget returned 0");
  
  // 检查b是否指向有效地址
  if((uint64)b < (uint64)bcache.buf || (uint64)b >= (uint64)(bcache.buf + NBUF)) {
    printf("bread: invalid buffer pointer %p (bcache.buf=%p, end=%p)\n", 
           b, bcache.buf, bcache.buf + NBUF);
    panic("bread: invalid buffer");
  }
    
  if(!b->valid) {
    // 在调用 ramdisk_rw 之前，确保缓冲区已锁定
    // bget 已经调用了 acquiresleep，所以这里应该已经锁定了
    if(use_ramdisk) {
      ramdisk_rw(b, 0);
    } else {
      virtio_disk_rw(b, 0);
    }
    b->valid = 1;
  }
  return b;
}

// 将缓存块写回磁盘
void
bwrite(struct buf *b)
{
  if(!holdingsleep(&b->lock))
    panic("bwrite");
  if(use_ramdisk) {
    ramdisk_rw(b, 1);
  } else {
    virtio_disk_rw(b, 1);
  }
  b->disk = 1;  // 标记为需要写回
}

// 释放一个缓存块
void
brelse(struct buf *b)
{
  struct proc *p = myproc();
  
  // 在正常阶段，必须持有锁
  if(p != 0) {
    if(!holdingsleep(&b->lock))
      panic("brelse");
  }
  // 初始化阶段：不检查锁，因为允许重复获取

  acquire(&bcache.lock);
  b->refcnt--;
  int refcnt_was_zero = (b->refcnt == 0);
  if (refcnt_was_zero) {
    // 移动到链表头部（最近使用）
    b->next->prev = b->prev;
    b->prev->next = b->next;
    b->next = bcache.head.next;
    b->prev = &bcache.head;
    bcache.head.next->prev = b;
    bcache.head.next = b;
  }
  release(&bcache.lock);
  
  // 只在 refcnt 减为 0 时释放锁（最后一个引用）
  if(refcnt_was_zero) {
    if(p != 0) {
      releasesleep(&b->lock);
    } else {
      // 初始化阶段：如果锁被持有，释放它
      if(holdingsleep(&b->lock)) {
        releasesleep(&b->lock);
      }
    }
  }
}

// 释放一个缓存块（不移动LRU位置）
void
bpin(struct buf *b) {
  acquire(&bcache.lock);
  b->refcnt++;
  release(&bcache.lock);
}

void
bunpin(struct buf *b) {
  acquire(&bcache.lock);
  b->refcnt--;
  release(&bcache.lock);
}